package com.learning.crpc.protocol;

import cn.hutool.core.util.ByteUtil;
import com.alibaba.fastjson.JSONObject;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;

import java.util.ArrayList;

/**
 * @program: diy_rpc
 * @description:
 * @author: CaoYong
 * @create: 2021-07-01 14:28
 **/
public class NettyCodec extends ChannelDuplexHandler {

    Class decodeType;

    public NettyCodec(Class decodeType) {
        this.decodeType = decodeType;
    }

    final static byte[] MAGIC = new byte[] {(byte) 0xda, (byte) 0xdd};

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf in = (ByteBuf) msg;
        ArrayList<Object> out = decode(in);
        for (Object o : out) {
            ctx.fireChannelRead(o);
        }
    }

    ByteBuf tempMsg = Unpooled.buffer();



    private ArrayList<Object> decode(ByteBuf in) throws Exception{

        ArrayList<Object> out = new ArrayList<>();
        tempMsg.writeBytes(in);

        for (;;) {
            if (tempMsg.readableBytes() <= 6) {
                byte[] data = new byte[tempMsg.readableBytes()];
                tempMsg.readBytes(data);
                tempMsg.clear();
                tempMsg.writeBytes(data);
                return out;
            }
            byte[] magic = new byte[2];
            tempMsg.readBytes(magic);
            while(true) {
                if (magic[0] != MAGIC[0] || magic[1] != MAGIC[1]) {
                    if (tempMsg.readableBytes() == 0) {
                        tempMsg.clear();
                        tempMsg.writeByte(magic[1]);
                        return out;
                    }
                    magic[0] = magic[1];
                    magic[1] = tempMsg.readByte();
                } else {
                    break;
                }
            }
            byte[] lengthBytes = new byte[4];
            tempMsg.readBytes(lengthBytes);
            int length = ByteUtil.bytesToInt(lengthBytes);

            if (tempMsg.readableBytes() < length) {
                byte[] data = new byte[tempMsg.readableBytes()];
                tempMsg.readBytes(data);
                tempMsg.clear();
                tempMsg.writeBytes(magic);
                tempMsg.writeBytes(lengthBytes);
                tempMsg.writeBytes(data);
                return out;
            }

            byte[] body = new byte[length];
            tempMsg.readBytes(body);

            String bodyStr = new String(body);
            Object o = JSONObject.parseObject(bodyStr, decodeType);
            out.add(o);
        }
    }

    @Override
    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
        byte[] body = JSONObject.toJSONBytes(msg);

        ByteBuf requestBuffer = Unpooled.buffer();
        requestBuffer.writeByte(MAGIC[0]);
        requestBuffer.writeByte(MAGIC[1]);

        int len = body.length;
        byte[] lenBytes = ByteUtil.intToBytes(len);
        requestBuffer.writeBytes(lenBytes);
        requestBuffer.writeBytes(body);
        ctx.write(requestBuffer);
    }
}
